<template>
  <div>
    <el-switch
      v-model="draggable"
      active-text="开启拖拽"
      inactive-text="关闭拖拽"
    ></el-switch>
    <el-button v-if="draggable" @click="batchSave">批量保存</el-button>
    <el-button type="danger" @click="batchDelete">批量删除</el-button>
    <el-tree
      :data="menus"
      :props="defaultProps"
      :expand-on-click-node="false"
      show-checkbox
      node-key="catId"
      :default-expanded-keys="expandedkey"
      :draggable="draggable"
      :allow-drop="allowDrop"
      @node-drop="handleDrop"
      ref="menuTree"
    >
    <!-- ref="menuTree"全选删除 -->
      <!-- :draggable="draggable"true 可以拖动 -->
      <!--  @node-drop="handleDrop"监听拖拽成功 -->
      <!-- show-checkbox全选   node-key=唯一子节点-->
      <span class="custom-tree-node" slot-scope="{ node, data }">
        <span>{{ node.label }}</span>
        <span>
          <!-- v-if="node.level <=2"字节点，小于等于2 -->
          <el-button
            v-if="node.level <= 2"
            type="text"
            size="mini"
            @click="() => append(data)"
          >
            Append
          </el-button>
          <el-button
            v-if="node.level <= 2"
            type="text"
            size="mini"
            @click="() => edit(data)"
          >
            edit
          </el-button>
          <!-- v-if="node.childNodes.length==0"数组的长度等于0 -->
          <el-button
            v-if="node.childNodes.length == 0"
            type="text"
            size="mini"
            @click="() => remove(node, data)"
          >
            Delete
          </el-button>
        </span>
      </span>
    </el-tree>
    <el-dialog
      :title="title"
      :visible.sync="dialogVisible"
      width="30%"
      :close-on-click-modal="false"
    >
      <!--:close-on-click-modal="false"  :是动态绑定一个值，要不然报错  -->
      <el-form :model="category">
        <el-form-item label="分类名称">
          <!-- // v-model双向绑定 -->
          <el-input v-model="category.name" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="图标">
          <!-- // v-model双向绑定 -->
          <el-input v-model="category.icon" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="计量单位">
          <!-- // v-model双向绑定 -->
          <el-input
            v-model="category.productUnit"
            autocomplete="off"
          ></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="submitData">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>
// 引入节流
import throatButton from "@/utils/th_bt";
<script>
//这里可以导入其他文件（比如：组件，工具js，第三方插件js，json文件，图片文件等等）
//例如：import 《组件名称》 from '《组件路径》';

export default {
  //import引入的组件需要注入到对象中才能使用
  components: {},
  props: {},
  //children返回字段的字节点
  data() {
    return {
      pCid: [], //父节点都存一个数组
      draggable: false, //默认false不可以拖拽
      updateNodes: [], //所有要修改的节点，都放到这里
      maxlevel: 1, //最大是1
      title: "",
      dialogType: "", //edit,add
      category: {
        name: "",
        parentCid: 0,
        catLevel: 0,
        showStatus: 1,
        sort: 0,
        productUnit: "",
        icon: "",
        catId: null,
      },
      dialogVisible: false,
      menus: [],
      expandedkey: [],
      defaultProps: {
        children: "children",
        label: "name",
      },
    };
  },

  //监听属性 类似于data概念
  computed: {},
  //监控data中的数据变化
  watch: {},
  //方法集合
  methods: {
    getMenus() {
      this.$http({
        url: this.$http.adornUrl("/product/category/list/tree"),
        method: "get", //then清求发送成功以后的函数 data写地{} 把参数内容结构出来
      }).then(({ data }) => {
        console.log("成功获取到菜数据..", data.data);
        this.menus = data.data;
      });
    },
    batchDelete() {
      let catIds = [];//当前元素数组
      let checkedNodes = this.$refs.menuTree.getCheckedNodes();
      console.log("被选中的元素", checkedNodes);
      //后端是删除数组，前端遍历
      //遍历到所有被选中元素的id
      for (let i = 0; i < checkedNodes.length; i++) {
        catIds.push(checkedNodes[i].catId);//当前元素id
      }
      this.$confirm(`是否批量删除【${catIds}】菜单?`, "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      })
        .then(() => {
          this.$http({
            url: this.$http.adornUrl("/product/category/delete"),
            method: "post",
            data: this.$http.adornData(catIds, false)
          }).then(({ data }) => {
            this.$message({
              message: "菜单批量删除成功",
              type: "success"
            });
            this.getMenus();
          });
        })
        .catch(() => {});
    },
    batchSave() {
      this.$http({
        url: this.$http.adornUrl("/product/category/update/sort"),
        method: "post",
        data: this.$http.adornData(this.updateNodes, false),
      }).then(({ data }) => {
        this.$message({
          message: "菜单顺序等修改成功",
          type: "success",
        });
        //刷新出新的菜单
        this.getMenus();
        //设置需要默认展开的菜单
        this.expandedKey = this.pCid; //用vue实例保存的pcid
        this.updateNodes = [];
        this.maxLevel = 0;
        //this.pCid = 0;
      });
    },
    //拖拽成功调用这几个参数 draggingNode：当前正在拖拽的节点, dropNode：进入那个节点, dropType：进入节点的什么位置
    handleDrop(draggingNode, dropNode, dropType, ev) {
      console.log("handleDrop: ", draggingNode, dropNode, dropType);
      //1、当前节点最新的父节点id
      let pCid = 0; //得到父类节点id
      let siblings = null; //兄弟的节点 顺序
      //如果是以兄弟方式进入 ，他的父id是兄弟父亲的id(就是从一个父亲的里面拖拽到另一个父亲的里面)
      if (dropType == "before" || dropNode == "after") {
        pCid =
          dropNode.parent.data.catId == undefined
            ? 0
            : dropNode.parent.data.catId; //得到父类节点id
        //通过前端拖拽显示 他的兄弟就是 这个节点的父亲的所有子节点
        siblings = dropNode.parent.childNodes;
      } else {
        //如果拖拽的是通一个父亲 id 的取值
        //如果获取的父亲节点 是undefined，页面上应该是跟节点，设置为0  否则就是原来的节点
        pCid = dropNode.data.catId;
        siblings = dropNode.childNodes;
      }
      //上面改掉了pcid 在这里给全局的pcid 保存
      this.pCid.push(pCid); //每修改一个菜单，父节点都放在这
      //2、当前拖拽节点的最新顺序，（拖拽一次，就是当前节点的所有子节点拿过来，重新排序，要遍历兄弟（情况不同也不一样））
      //遍历  siblings.length兄弟的长度
      for (let i = 0; i < siblings.length; i++) {
        //catId:代表我当前要改那个节点,sort: i节点的顺序按照遍历的顺序来 i
        //最终将整个内容放进 updateNnodes里面
        //这是一个数组，直接点 push 把他方进去
        //当前拖拽的节点还可能影响父子关系
        //如果当前遍历的节点，是我拖拽的节点,siblings[i].data.catId这就是当前遍历节点的id
        //draggingNode这就是当前拖拽的节点 ，的data里面有他的id
        if (siblings[i].data.catId == draggingNode.data.catId) {
          //如果遍历的是当前正在拖拽的节点
          //如果遍历是当前节点，他的层级要发生变化  catLevel/当前节点的层级
          //以前拖拽的时候有一个默认层级，如果两个层级不一样，说明，当前节点层级发生变化
          //现在的，--以前默认的
          //假设有一个默认层级catlevel、、Level正在拖拽节点的level
          let catLevel = draggingNode.data.level;
          if (siblings[i].level !== draggingNode.level) {
            //正在遍历的节点层级和正在拖拽的节点层级不一样
            //当前节点的层级发生变化，那就要修改当前节点的层级信息
            //层级发生变化，就拿到最新层级
            //判断层级关系//如果是前后关系 那层级 就是和当前正在拖拽元素的层级是一样的
            // if(dropType == "before"|| dropNode=="after"){
            //   catLevel = dropNode.level;
            // }else{//否则是inner  就是在里面就是给他的层级加1
            //    catLevel = dropNode.level + 1;
            // }
            //当前节点的层级和他真正的内容同步
            //catLevel = siblings[i].level当前节点的层级，等于正在遍历节点的层级
            catLevel = siblings[i].level;
            //修改他子节点的层级（子节点，可能还有其他子节点，用递归操作）
            //要修改子节点的层级，还要将子节点的层级收集起来
            this.updateChildNodeLevel(siblings[i]); //修改他里面所有子节点的层级
          }
          //parentCid父id 值不应该是以前的父亲id值，应该是最新的父亲id值  /pCid上面获取到的父亲id值
          //两个父亲 子 拖拽 要改顺序，还要改父亲id
          //如果层级发生变化 ，就改层级
          this.updateNodes.push({
            catId: siblings[i].data.catId,
            sort: i,
            parentCid: pCid,
            catLevel: catLevel,
          }); //表示在处理那个兄弟，将我们要改的信息封装到对象里面
        } else {
          //不只要排序，还要方最新的父亲id  （其他兄弟id只改顺序，他自己还要改父类id）
          //兄弟的子节点拖拽只改顺序
          this.updateNodes.push({ catId: siblings[i].data.catId, sort: i }); //表示在处理那个兄弟，将我们要改的信息封装到对象里面
        }
      }
      //3、当前拖拽节 点的最新层级
      console.log("updateNodes", this.updateNodes);
      console.log(pCid);
    },
    updateChildNodeLevel(node) {
      if (node.childNodes.length > 0) {
        for (let i = 0; i < node.childNodes.length; i++) {
          var cNode = node.childNodes[i].data;
          this.updateNodes.push({
            catId: cNode.catId,
            catLevel: node.childNodes[i].level,
          });
          this.updateChildNodeLevel(node.childNodes[i]);
        }
      }
    },
    //draggingNode:当前节点, dropNode：放到那个节点的那些位置, type：那个位置
    allowDrop(draggingNode, dropNode, type) {
      //1.被拖动的当前节点以及所在的父节点总层数不能大于3
      //(1)判断被拖动当前节点的总层数(如果当前节点有子节点，怎末判断，就.children 有子节点就有children)
      console.log(draggingNode, dropNode, type);
      this.countNodelevel(draggingNode); //直接将当前节点传进来//统计当前节点的总层数 节点放在data里面，所以要.dat
      //最大深度加上，
      //当前正在拖拽节点加上父节点所在的深度不大于3就好（总深度是3）
      //console.log("深度：",this.maxlevel);
      //他的深度是，子节点的最大level减去当前子节点的level+ 1
      //deep是当前节点的深度
      let deep = Math.abs(this.maxlevel - draggingNode.level) + 1; //math计算出绝对值
      console.log("深度：", deep);
      //this.maxlevel;
      //dropNode被拖到那个位置 级数
      //如果拖动到里面是相加的关系
      //dropNode这个节点有个属性叫level
      if (type == "inner") {
        return deep + dropNode.level <= 3;
        // console.log(
        //   `this.maxlevel: ${this.maxlevel};draggingNode.data.catLevel: ${draggingNode.data.catLevel};dropNode.parent.level: ${dropNode.parent.level}`
        // );
      } else {
        return deep + dropNode.parent.level <= 3;
      }
    },
    countNodelevel(node) {
      //找到所有子节点，求出最大深度 childNodes//拿到数的真正子节点
      if (node.childNodes != null && node.childNodes.length > 0) {
        //children是一个数组，数组的长度大于0
        for (let i = 0; i < node.childNodes.length; i++) {
          //遍历当前节点的长度
          if (node.childNodes[i].level > this.maxlevel) {
            //如果当前节点的长度大于最大的节点的长度
            //就给他们交换值，最大的等于当前节点的深度值
            //这是一个递归，//每一个节点深度大于当前节点就给她赋值
            this.maxlevel = node.childNodes[i].level; //maxlevel是自己定义统计最大层级
          }
          //同时看自己遍历的节点node.children[i].catLevel 又有没有子节点。
          //继续调用这个方法，，看看当前调用这个节点还有没有其他子节点
          this.countNodelevel(node.childNodes[i]); //就是循环或者递归调用看看当前调用这个节点还有没有其他子
          //就是判断node.children[i]是不是空的，不是就继续加
        }
      }
    },
    edit(data) {
      console.log("要修改的数据", data);
      this.dialogType = "edit";
      this.title = "修改分类";
      this.dialogVisible = true;
      //发送请求获取当前节点的最新数据
      this.$http({
        //` 反引号动态去到id
        url: this.$http.adornUrl(`/product/category//info/${data.catId}`),
        method: "get",
        params: this.$http.adornParams({}),
      }).then(({ data }) => {
        //请求成功
        this.category.name = data.data.name;
        this.category.catId = data.data.catId;
        this.category.icon = data.data.icon; //图标
        this.category.productUnit = data.data.productUnit; //计量单位
        this.category.parentCid = data.data.parentCid; //计量单位 回显parentCid  id
        this.category.catLevel = data.data.catLevel;
        this.category.sort = data.data.sort;
        this.category.showStatus = data.data.showStatus;
        /**
         *         parentCid: 0,
        catLevel: 0,
        showStatus: 1,
        sort: 0,
         */
      });
    },
    append(data) {
      console.log("append", data);
      this.dialogType = "add";
      this.title = "添加分类";
      this.dialogVisible = true;
      this.category.parentCid = data.catId;
      //catLevel*1 + 1  当前层级的下一层级，害怕他是字符串，先乘1变成数字
      this.category.catLevel = data.catLevel * 1 + 1;
      this.category.catId = null; //不带id添加因为id是自增的
      this.category.name = ""; //要添加的内容设置空//后面表单输入啥，再默认绑定就好
      this.category.icon = ""; //要添加的内容设置空
      this.category.productUnit = ""; //要添加的内容设置空
      this.category.sort = 0; //自增的设置0
      this.category.showStatus = 1; //显示状态默认1让他显示的
    },

    submitData() {
      if (this.dialogType == "add") {
        this.addCategory();
      }
      if (this.dialogType == "edit") {
        this.editCategory();
      }
    },
    //修改三级分类的方法
    editCategory() {
      //结构自己需要的属性名字和值
      var { catId, name, icon, productUnit } = this.category;
      //var data = {catId: catId,name: name,icon: icon,productUnit: productUnit} //赋值 k v 是同一个值  ： 可以省略
      //var data =  //赋值
      this.$http({
        url: this.$http.adornUrl("/product/category/update"),
        method: "post",
        data: this.$http.adornData({ catId, name, icon, productUnit }, false), //发送出去的数据
      }).then(({ data }) => {
        this.$message({
          message: "菜单修改成功",
          type: "success",
        });
        //关闭对话框
        this.dialogVisible = false;
        //刷新出新的菜单
        this.getMenus();
        //设置需要默认展开的菜单
        this.expandedkey = [this.category.parentCid];
      });
    },
    //添加三级分类的方法
    addCategory() {
      console.log("提交三级分类的数据", this.category);
      this.$http({
        url: this.$http.adornUrl("/product/category/save"),
        method: "post",
        data: this.$http.adornData(this.category, false),
      }).then(({ data }) => {
        this.$message({
          message: "菜单保存成功",
          type: "success",
        });
        //关闭对话框
        this.dialogVisible = false;
        //刷新出新的菜单
        this.getMenus();
        //设置需要默认展开的菜单
        this.expandedkey = [this.category.parentCid];
      });
    },
    remove(node, data) {
      var ids = [data.catId]; //` `插值表达式
      this.$confirm(`是否刪除【${data.name}】菜单？`, "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          this.$http({
            url: this.$http.adornUrl("/product/category/delete"),
            method: "post",
            data: this.$http.adornData(ids, false),
          }).then(({ data }) => {
            this.$message({
              message: "菜单删除成功",
              type: "success",
            });
            //刷新出的菜单
            this.getMenus(); //getMenus重新请求参数
            //设置需要展开的菜单
            this.expandedkey = [node.parent.data.catId];
          });
        })
        .catch(() => {});

      console.log("remove", node, data);
    },
  },
  //生命周期 - 创建完成（可以访问当前this实例）
  created() {
    //一创建组件，就获取菜单数据
    this.getMenus();
  },
  //生命周期 - 挂载完成（可以访问DOM元素）
  mounted() {},
  beforeCreate() {}, //生命周期 - 创建之前
  beforeMount() {}, //生命周期 - 挂载之前
  beforeUpdate() {}, //生命周期 - 更新之前
  updated() {}, //生命周期 - 更新之后
  beforeDestroy() {}, //生命周期 - 销毁之前
  destroyed() {}, //生命周期 - 销毁完成
  activated() {}, //如果页面有keep-alive缓存功能，这个函数会触发
};
</script>
<style scoped>
</style>


